#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "BMP_struct.h"
#include "HuffTree.h"
#include "Utils.h"

#define BYTELENGTH 8

int main(int arg, char** argv)
{
    int tam = 0, pixelNumber, i,j;
    FILE *arquin, *arqout;
    BMPFILEHEADER *H;
    BMPINFOHEADER *IH;
    CONTENTIMAGE  *C;
    FileContent *fileContent;
    Node *noR, *noG, *noB;
    char antR, antG, antB;
    HuffTree *huffTree;
    HuffNo *huffNo, **leafNodes;
    int *frequenciVector, leafNumber;
    HuffmanCodes *huffmanCodes;
    ByteSequence *byteSequence;
    unsigned char numOfCodes, byte;
    short int numOfCodesI;
    NoByte *noByte;
    char *stringHuff, *stringByte;
    int encontrou, tamanho, bit;


    if(strcmp(argv[1], "-c") == 0) //./nomePrograma -c arquin arquout
    {
        arquin = fopen(argv[2], "rb");
        arqout = fopen(argv[3], "wb");
        if(!arquin || !arqout)
        {
            printf("\nAquivo de entrada ou saida com algum problema\n");
            return 1;
        }

        H = (BMPFILEHEADER *)malloc(sizeof(BMPFILEHEADER));
        IH = (BMPINFOHEADER *)malloc(sizeof(BMPINFOHEADER));
        readFileHeader(arquin,H); //le cabecalho do arquivo
        readInfoHeader(arquin,IH);  //le cabecalho de informacao do arquivo

        i=0;
        tam = IH->biHeight * IH->biWidth;
        C = (CONTENTIMAGE*) malloc(tam*sizeof(CONTENTIMAGE));
        while(i< tam)
        {
            readContenImage(arquin, &C[i]);
            i++;
        }

        pixelNumber = IH->biHeight*IH->biWidth; //numero de pixels na imagem

        fileContent = (FileContent*)malloc(sizeof(FileContent));
        init(fileContent);

        noR = (Node*) malloc(sizeof(Node));//cria os primeiros nos da lista
        noG = (Node*) malloc(sizeof(Node));
        noB = (Node*) malloc(sizeof(Node));

        antR = noR->bits = noR->valueDec = C[0].Red;//seta valores deles, valores iniciais da codificacao por diferenca
        antG = noG->bits = noG->valueDec = C[0].Green;
        antB = noB->bits = noB->valueDec = C[0].Blue;

        noR->numBits = tamBin(noR->bits); //verificar o retorno dessa funcao
        noG->numBits = tamBin(noG->bits);
        noB->numBits = tamBin(noB->bits);


        noR->huffCode = 0;
        noR->huffCodeTam =0;
        noR->next = 0;
        noG->huffCode = 0;
        noG->huffCodeTam =0;
        noG->next = 0;
        noB->huffCode =0;
        noB->huffCodeTam =0;
        noB->next = 0;

        pushF(fileContent, noR);//insere nos na lista q tem o conteudo do arquivo
        pushF(fileContent, noG);
        pushF(fileContent, noB);

        for(i=1; i< tam; i++) //para cada pixel do arquivo, calculamos a diferença somente entre os componentes: R com R, G com G e B com B
        {
            noR = (Node*) malloc(sizeof(Node));
            noG = (Node*) malloc(sizeof(Node));
            noB = (Node*) malloc(sizeof(Node));

            noR->bits = noR->valueDec = antR - C[i].Red;//seta valores deles, valores iniciais da codificacao por diferenca
            noG->bits = noG->valueDec = antG - C[i].Green;
            noB->bits = noB->valueDec = antB - C[i].Blue;

            noR->numBits = tamBin(noR->bits); //verificar o retorno dessa funcao
            noG->numBits = tamBin(noG->bits);
            noB->numBits = tamBin(noB->bits);

            noR->huffCode = 0;
            noR->huffCodeTam = 0;
            noR->next = 0;
            noG->huffCode =0;
            noG->huffCodeTam = 0;
            noG->next = 0;
            noB->huffCode = 0;
            noB->huffCodeTam =0;
            noB->next = 0;

            pushF(fileContent, noR);//insere nos na lista q tem o conteudo do arquivo
            pushF(fileContent, noG);
            pushF(fileContent, noB);

            antR = C[i].Red;
            antG = C[i].Green;
            antB = C[i].Blue;
        }

        //calculo do codigo huffman para os tamanhos dos bits dos numeros
        huffTree = (HuffTree*)malloc(sizeof(HuffTree));
        initHuff(huffTree);

        frequenciVector = (int*) malloc(BYTELENGTH*sizeof(int)); //conta frequencia dos tamanhos
        for(i=0; i< BYTELENGTH; i++)
        {
            frequenciVector[i] = 0;
        }

        noR = getStart(fileContent);//conta frequencia dos tamanhos
        while(noR != 0)
        {
            frequenciVector[noR->numBits -1]++;
            noR = noR->next;
        }

        leafNodes = (HuffNo**)malloc(BYTELENGTH*sizeof(HuffNo*)); //cria nos folha da arvore de huffman
        leafNumber =0;
        j=0; //indice para armazenar no leafNode
        for(i=0; i< BYTELENGTH; i++)
        {
            if(frequenciVector[i] != 0)
            {
                leafNumber++; //armazena o numero de nos folha
                huffNo = (HuffNo*)malloc(sizeof(HuffNo));
                huffNo->folha =1;
                huffNo->frequencia = frequenciVector[i]; //frequencia para esse tamanho
                huffNo->tamanho = i+1; //tamanho em bits do numero
                huffNo->codigoHuffTam = 0; //variaveis utilizadas na construcao da arvore de huffman
                huffNo->codigoHuff = 0;
                huffNo->dir = 0;
                huffNo->esq = 0;

                leafNodes[j] = huffNo;
                j++;
            }
        }
        printf("Treehuffman");
        constructTree(huffTree,leafNodes,leafNumber); //depois dessa funcao ja temos os codigo huffman pra escreve em arquivo
        printf("write file");
        writeFile_emanuel(arqout, H, IH, fileContent, leafNumber, huffTree);




        printf("%d - - altura\n",IH->biHeight);
        printf("%d - - largura\n",IH->biWidth);
        printf("%d --pixelNumber\n",pixelNumber);
        printf("%d --tam\n",tam); //tam = 786433, 1 a mais para o EOF, mas vale so 786432 posicoes no vetor C



        fclose(arqout);
        fclose(arquin);

	}
    else if(strcmp(argv[1], "-d") == 0) //descompressao do arquivo gerado, ./main -d arquin arqout
    {
        arquin = fopen(argv[2], "rb");
        arqout = fopen(argv[3], "wb");
        if(!arquin || !arqout)
        {
            printf("\nAquivo de entrada ou saida com algum problema\n");
            return 1;
        }

        H = (BMPFILEHEADER *)malloc(sizeof(BMPFILEHEADER));
        IH = (BMPINFOHEADER *)malloc(sizeof(BMPINFOHEADER));

        //le cabecalho do arquivo de entrada
        fread(&numOfCodes, sizeof(unsigned char), 1,arquin); //contem numero de codigos huffman
        numOfCodesI = (short int)numOfCodes;
        huffmanCodes = (HuffmanCodes*) malloc(sizeof(HuffmanCodes)*numOfCodesI);

        for(i=0; i< numOfCodesI; i++) //le cabecalho huffman
        {
            fread(&byte, sizeof(unsigned char),1, arquin); //tamanho em bits do codigo huffman
            huffmanCodes[i].codeHuffTam = (short int)byte;
            printf("===%d\n",byte);

            fread(&byte,sizeof(unsigned char),1,arquin); //cogigo huffman gerado
            huffmanCodes[i].codeHuff = byte;
            fread(&byte,sizeof(unsigned char),1, arquin); //tamanho em bits correspondente
            huffmanCodes[i].tamanho = (short int)byte;
            //preenche formato em string do codigo huffman, funcao de short int para char*
            huffmanCodes[i].codeHuffString = intToString(huffmanCodes[i].codeHuff, huffmanCodes[i].codeHuffTam);
        }

        //leitura do header e info header
        fread(&H->Type,sizeof(char),2,arquin);
        fread(&H->Size,sizeof(unsigned int),1,arquin);
        fread(&H->Reserved1,sizeof(unsigned short int),1,arquin);
        fread(&H->Reserved2,sizeof(unsigned short int),1,arquin);
        fread(&H->OffBits,sizeof(unsigned int),1,arquin);

        //leitura do info header
        fread(&IH->biSize,sizeof(unsigned int),1,arquin);
        fread(&IH->biWidth,sizeof(int),1,arquin);
        fread(&IH->biHeight,sizeof(int),1,arquin);
        fread(&IH->biPlanes,sizeof(unsigned short int),1,arquin);
        fread(&IH->biBitCount,sizeof(unsigned short int),1,arquin);
        fread(&IH->biCompression,sizeof(unsigned int),1,arquin);
        fread(&IH->biSizeImage,sizeof(unsigned int),1,arquin);
        fread(&IH->biXPelsPerMeter,sizeof(int),1,arquin);
        fread(&IH->biYPelsPerMeter,sizeof(int),1,arquin);
        fread(&IH->biClrUsed,sizeof(unsigned int),1,arquin);
        fread(&IH->biClrImportant,sizeof(unsigned int),1,arquin);

        //le conteudo comprimido
        byteSequence = (ByteSequence*) malloc(sizeof(ByteSequence));
        initB(byteSequence);
        int teste = 0;
        int ret;
        while(!feof(arquin)) //pega byte por byte do conteudo comprimido, pega o ultimo byte , o EOF
        {
            noByte = (NoByte*) malloc(sizeof(NoByte));
            ret =  fread(&byte, sizeof(unsigned char), 1, arquin);
            noByte->byte = byte;
            noByte->last = 0;
            noByte->next = 0;
            push(byteSequence, noByte);
            noByte = NULL;
            teste++;
        }

        //grava conteudo do cabecalho header e infoheader
        fwrite(&H->Type,sizeof(char),2,arquin);
        fwrite(&H->Size,sizeof(unsigned int),1,arquin);
        fwrite(&H->Reserved1,sizeof(unsigned short int),1,arquin);
        fwrite(&H->Reserved2,sizeof(unsigned short int),1,arquin);
        fwrite(&H->OffBits,sizeof(unsigned int),1,arquin);

        //grava o info header
        fwrite(&IH->biSize,sizeof(unsigned int),1,arquin);
        fwrite(&IH->biWidth,sizeof(int),1,arquin);
        fwrite(&IH->biHeight,sizeof(int),1,arquin);
        fwrite(&IH->biPlanes,sizeof(unsigned short int),1,arquin);
        fwrite(&IH->biBitCount,sizeof(unsigned short int),1,arquin);
        fwrite(&IH->biCompression,sizeof(unsigned int),1,arquin);
        fwrite(&IH->biSizeImage,sizeof(unsigned int),1,arquin);
        fwrite(&IH->biXPelsPerMeter,sizeof(int),1,arquin);
        fwrite(&IH->biYPelsPerMeter,sizeof(int),1,arquin);
        fwrite(&IH->biClrUsed,sizeof(unsigned int),1,arquin);
        fwrite(&IH->biClrImportant,sizeof(unsigned int),1,arquin);

        //pega cada bit contido em byteSequence e verifica se eh um codigo huffman, depois pega conteudo do numero
        stringHuff = (char*) malloc(sizeof(char)*30);
        stringHuff[0] ='\0';
        stringByte = (char*) malloc(sizeof(char)*10);
        stringByte[0] = '\0';
        bit = getNextBitValue(byteSequence);
        while(bit != -1) //enquanto tem bit na byteSequence
        {
            if(bit ==1)
            {
                strcat(stringHuff, "1");
            }
            else //bit ==0
            {
                strcat(stringHuff, "0");
            }
            if(strlen(stringHuff)>8)
            {
                printf("Problema em stringHuff para encontrar o codigo huffman\n");
            }
            //procura stringHuff, seta flag encontrou e pega tamanho em bits correspondente
            encontrou =0;
            for(i=0; i< numOfCodesI; i++) //para todos codigoHuffman em huffmanCodes
            {
                if(strcmp(stringHuff, huffmanCodes[i].codeHuffString) == 0) //encontrou o codigo huffman
                {
                    encontrou =1;
                    tamanho = huffmanCodes[i].tamanho;
                }
            }

            if(encontrou) //pega o numero de bits correspondentes ao codigo huffman e grava byte em arquivo
            {
                stringByte[0] ='\0';
                for(i=0; i< tamanho; i++)
                {
                    bit = getNextBitValue(byteSequence);
                    if(bit == 1)
                    {
                        strcat(stringByte, "1");
                    }
                    else if(bit == 0)
                    {
                        strcat(stringByte, "0");
                    }
                }

                //grava byte em arquivo
                byte = stringBinToByte(stringByte);

                printf("write file");
                fwrite(&byte,sizeof(char),1,arqout);
                stringHuff[0] = '\0';
            }

            bit = getNextBitValue(byteSequence);
        }

        fclose(arqout);
        fclose(arquin);
    }

    return 0;
}
